package org.bbelovic.devel.pexeso.gui;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.Collection;
import java.util.List;

import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.event.MouseInputAdapter;

/**
 * Class holding all UI components for Pairs game.
 */
public class Frame extends JFrame implements GameListener {

    private static final int FRAME_HEIGHT = 400;
    private static final int FRAME_WIDTH = 600;
    private static final long serialVersionUID = -6564185370146498321L;
    static final String IMAGE_DIR = "/home/bbelovic/workspace/pexeso-gui/src/main/resources";
    static final String RESOURCES_DIR = "../../../src/main/resources";
    
    private final JPanel infoPanel = new JPanel();
    private final JPanel playerPanel = new JPanel();
    private final JButton switchButton = new JButton("Next player");
    private final JButton addPlayerButton = new JButton("Add player");
    private final FlagLabelsManager flagManager = new FlagLabelsManager();
    private final GameController controller = new GameController();

    /**
     * Constructs UI for application. Registers all listeners and place all
     * panels to main screen.
     */
    public Frame() {
        flagManager.setMouseListener(new PairsMouseListener());
        controller.setFlagManager(flagManager);
        controller.addGameListener(this);

        // UI part
        setSize(FRAME_WIDTH, FRAME_HEIGHT);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setTitle("Pairs");
        
        final JPanel gamePanel = createGamePanel(flagManager.prepareFlagLabels());
        final JPanel infoPanel = createInfoPanel();
        final Container c = getContentPane();
        c.add(gamePanel, BorderLayout.CENTER);
        c.add(infoPanel, BorderLayout.LINE_END);
        setResizable(false);
        setVisible(true);
    }

    /**
     * Updates components when necessary. This method is called by classes which have
     * registered instances of {@link GameListener} as observers.
     */
    @Override
    public void updateComponents() {
        switchButton.setEnabled(true);
    }

    /**
     * Creates game panel which holds labels with flag images.
     * @param labels collection of labels holding flag images
     * @return game label prepared to be placed on main screen
     */
    private JPanel createGamePanel(final Collection<JLabel> labels) {
        final JPanel gamePanel = new JPanel(new FlowLayout(FlowLayout.LEFT));
        for (final JLabel eachLabel: labels) {
            gamePanel.add(eachLabel);
        }
        return gamePanel;
    }
    
    /**
     * Constructs information panel which holds player information and buttons
     * for switching player and adding new player.
     * @return information panel prepared to be placed on main screen.
     */
    private JPanel createInfoPanel() {
        playerPanel.setLayout(new BoxLayout(playerPanel, BoxLayout.PAGE_AXIS));
        playerPanel.setPreferredSize(new Dimension(200, 100));
        infoPanel.add(playerPanel);
        infoPanel.setPreferredSize(new Dimension(200, 300));
        switchButton.addActionListener(new SwitchButtonActionListener());
        switchButton.setEnabled(false);
        infoPanel.add(switchButton);
        addPlayerButton.addActionListener(new AddPlayerButtonActionListener());
        infoPanel.add(addPlayerButton);
        return infoPanel;
    }

    /**
     * Register {@link Player} with {@link GameController} instance and adds label
     * with player's name to player information label.
     * @param player {@link Player} to be registered
     */
    void addPlayerLabel(final Player player) {
        controller.addPlayer(player);

        final JLabel playerLabel = new JLabel(player.getName());
        playerLabel.setAlignmentX(CENTER_ALIGNMENT);
        playerLabel.setAlignmentY(CENTER_ALIGNMENT);
        playerLabel.setName(player.getName());
        playerPanel.add(playerLabel);
    }

    // listener inner classes
    /**
     * {@link MouseAdapter} which flips labels with flag images when their are clicked
     * by player.
     */
    private class PairsMouseListener extends MouseInputAdapter {

        @Override
        public void mouseClicked(MouseEvent evt) {
            handleMouseClicked(evt);
        }
        
        private void handleMouseClicked(MouseEvent e) {
            if (!controller.hasPlayers()) {
                Utilities.showErrorMessage("No players", "No players registered for game!");
                return;
            }
            Player p = controller.getActualPlayer();
            Object source = e.getSource();
            JLabel label = (JLabel) source;
            String flagName = label.getName();
            if (!controller.verifyFlagLabel(flagName)) {
                
                System.out.println("Player " + p + " clicked twice on same flag.");
                return;
            }
            if (p.getActionPoints() > 0) {
                controller.evaluateFlagLabels(flagName);
            }
            updatePlayers(controller.getPlayers());
        }

        private Component getComponentByName(final String componentName, final Container container) {
            final Component [] components = container.getComponents();
            for (final Component eachComponent: components) {
                if (componentName.equals(eachComponent.getName())) {
                    return eachComponent;
                }
            }
            return null;
        }
        
        private void updatePlayers(final List<Player> players) {
            for (final Player eachPlayer: players) {
                Component comp = getComponentByName(eachPlayer.getName(), playerPanel);
                if (comp instanceof JLabel) {
                    JLabel label = (JLabel) comp;
                    final String text = eachPlayer.getName() + ": "+ eachPlayer.getScorePoints();
                    label.setText(text);
                }
            }
        }
    } //PairsMouseListener class

    /**
     * {@link ActionListener} responsible for switching players during the game.
     */
    private class SwitchButtonActionListener implements ActionListener {

        public void actionPerformed(ActionEvent evt) {
            flagManager.turnBlackIconOn();
            controller.getActualPlayer().emptyPairs();
            controller.getActualPlayer().restoreActionPoints();
            final Player previous = controller.getActualPlayer();
            controller.nextPlayer();
            final String msg = "Switching "+ previous + " -> " + controller.getActualPlayer();
            System.out.println(msg);
            switchButton.setEnabled(false);
        }
    }

    /**
     * {@link ActionListener} handling adding labels with player names to main screen.
     */
    private class AddPlayerButtonActionListener implements ActionListener {
        @Override
        public void actionPerformed(final ActionEvent evt) {
            new AddPlayerDialog(Frame.this);
        }
    } // AddPlayerButtonActionListener class

}
